#include "tecan.h"
#include "string.h"
#include "common.h"
#include <stdio.h>
#include <unistd.h>
#include "can_uim.h"
#include "msg_def.h"

CTecan::CTecan()
{
   m_currStatus =  tecan_idle;
   memset(m_recvBuff,0,MAX_TECAN_BUFF_LEN);
   m_recvLen = 0;
   pthread_mutex_init(&m_memMutex,NULL);
   m_exitThreadFlag = 0;
   m_funcComplteCB = NULL;
   m_liquid_detected = 0;
}

CTecan::~CTecan()
{
   m_currStatus =  tecan_idle;
   memset(m_recvBuff,0,MAX_TECAN_BUFF_LEN);
   m_recvLen = 0;
   m_funcComplteCB = NULL;
   m_exitThreadFlag = 1;
   usleep(30000);
   pthread_mutex_destroy(&m_memMutex);
   
}

void CTecan::addToBuff(unsigned char* ptrBuff, int len)
{
#if _DEBUG_LOG
    printf("recv from tecan,data len:%d\n",len);
    for(int i=0;i<len;i++)
    {
         printf("%02x,",ptrBuff[i]);
    }
    printf("\n");
#endif 
    if((m_recvLen+len) < MAX_TECAN_BUFF_LEN)
    {
       pthread_mutex_lock(&m_memMutex);
       memcpy(m_recvBuff+m_recvLen,ptrBuff,len);
       m_recvLen += len;
       pthread_mutex_unlock(&m_memMutex);
    }
    
}


void CTecan::setCurrStatus(int statusIn)
{
   m_currStatus = statusIn; 
}

int CTecan::getCurrStatus(void)
{
   return m_currStatus;
}


int CTecan::parseFrame(unsigned char* ptrBuff, int len,tecanRecvMsg** msgOut)
{

     int retValue = 1;
     if(len<7)
     {
         return -1; 
     }

     if(ptrBuff[0]!=0x02 || ptrBuff[len-2]!=0x03)
     {
        return -1;
     }

     if(msgOut==NULL)
     {
        return -1;
     }
     *msgOut = new tecanRecvMsg;

     memset(*msgOut,0,sizeof(tecanRecvMsg));

     switch (ptrBuff[4])
     {
       case '@':
       {
          (*msgOut)->resType = SYS_ACK_RES;
          (*msgOut)->un_res.stResSysAck.cmdId = ptrBuff[3];
          retValue = 0; 
       }
       break;
       case '%':
       {
          (*msgOut)->resType = CMD_STR_COMP_RES;
          (*msgOut)->un_res.stResCmdStrComp.cmdId = ptrBuff[3];
          (*msgOut)->un_res.stResCmdStrComp.devId = ptrBuff[5];
          memcpy((*msgOut)->un_res.stResCmdStrComp.devStatus, ptrBuff+6, 4); 
          retValue = 0;          
       }
       break;
       case '?':
       {
          if(ptrBuff[5] == 'S')
          {
            (*msgOut)->resType = SYS_QUERY_RES;
            (*msgOut)->un_res.stResSysQuery.cmdId = ptrBuff[3];
            memcpy((*msgOut)->un_res.stResSysQuery.error_code, ptrBuff+6,4);
            int dataLen = len-12;
            if(dataLen>0)
            {
               memcpy((*msgOut)->un_res.stResSysQuery.data,ptrBuff+10, dataLen);
            } 
                         
          }
          else
          {
            (*msgOut)->resType = DEV_QUERY_RES;
            (*msgOut)->un_res.stResDevQuery.cmdId = ptrBuff[3];
            (*msgOut)->un_res.stResDevQuery.devId = ptrBuff[5];
            memcpy((*msgOut)->un_res.stResDevQuery.devSatus,ptrBuff+6,4);
            int dataLen = len-12;

            if(dataLen>0)
            {
               memcpy((*msgOut)->un_res.stResDevQuery.data,ptrBuff+10, dataLen);
            } 
          }
          retValue = 0;         
       }
       break;
       default:
       break;
     }
     if(retValue)
     {
        delete *msgOut;
        *msgOut = NULL;
     }
     else if((*msgOut)->resType == SYS_ACK_RES && len > 7)
     {
        int ret = CTecan::parseFrame(ptrBuff+7,len-7,&((*msgOut)->next));
        if(ret)
        {
           (*msgOut)->next = NULL;
        }
     }

     return retValue;
}

void CTecan::encap_cmd_str(tecanCmdBlock * ptrBlockList,unsigned char * buffOut, int* lenOut)
{
   
}

void CTecan::setZMoveStr(void * moveParamIn, unsigned char * buffOut,int *lenOut)
{
   move_param * pMoveParam = (move_param *)moveParamIn;
   const char * strHead = "1/1b50c50";
   int tempLen = 0;
   
   memcpy(buffOut,strHead,strlen(strHead));
   tempLen +=  strlen(strHead);

   char tempBuff[128];
   //setting the acc.
   int n = sprintf(tempBuff,"a%d",pMoveParam->inc_speed);
   memcpy(buffOut+tempLen,tempBuff,n);
   tempLen += n;

   //setting the speed
   n = sprintf(tempBuff,"s%d",pMoveParam->speed);
   memcpy(buffOut+tempLen,tempBuff,n);
   tempLen += n;
   
   //setting the position
   n = sprintf(tempBuff,"A%d",pMoveParam->pos);
   memcpy(buffOut+tempLen,tempBuff,n);
   tempLen += n;


   buffOut[tempLen++] = 'E';
   buffOut[tempLen] = '\0';

   *lenOut =  tempLen;

#if _DEBUG_LOG
    printf("string to tecan:%s\n",buffOut);
#endif
    encap_tecan_frame(buffOut,tempLen,lenOut);
}


void CTecan::encap_tecan_frame(unsigned char * buffInOut,int lenIn,int * lenOut)
{
   memmove(buffInOut+3,buffInOut,lenIn);
   buffInOut[0] = SOCA_FRAME_HEADER;
   buffInOut[1] = SOCAID;
   buffInOut[2] = SEQNUM;

   buffInOut[lenIn + 3] = SOCA_FRAME_TAIL;

   buffInOut[lenIn+4] = buffInOut[0];//checksum, CAN no need

	for (int i = 1; i < (lenIn+4); i++)
   {
       buffInOut[lenIn+4] = (buffInOut[lenIn+4] ^ buffInOut[i]);
   }

   * lenOut = lenIn + 5;
   
}

void CTecan::setInitSOCAStr(int liquidType,unsigned char * buffOut,int * lenOut)
{
   int arrInitCode[3] = {70,71,72};
   
   //const char * initSocaStr = "1(/0U71WR/1b20c20J50>60=60<30d0H500>90=90E)";
   if(liquidType<0 || liquidType>2)
   {
      *lenOut = 0;
      return;
   }
   sprintf((char *)buffOut,"%s%d%s","1(/0U",arrInitCode[liquidType],
   "WR/1b20c20J50>60=60<30d0H500>90=90E)");
   int lenStr = strlen((char*)buffOut);
   encap_tecan_frame(buffOut,lenStr,lenOut);
}

void CTecan::setResetAdpStr(unsigned char * buffOut,int *lenOut)
{
   sprintf((char *)buffOut,"%s","1/0!R");
   int lenStr = strlen((char*)buffOut);
   encap_tecan_frame(buffOut,lenStr,lenOut);
}


void CTecan::setInitAdp_Z_Str(unsigned char * buffOut,int * lenOut)
{
   const char * initSocaStr = "1(/0U71WR/1b20c20J50>100=100<30d0H367>120=120E)";
   int lenStr = strlen(initSocaStr);
   sprintf((char *)buffOut,"%s",initSocaStr);

   encap_tecan_frame(buffOut,lenStr,lenOut);
}

void CTecan::setLiquidDetect(int isDetected)
{
    m_liquid_detected = isDetected;
}

void * CTecan::funcProcThread(void * paramIn)
{
   CTecan * pCTecan = (CTecan *)paramIn;
   #define MAX_WAIT_ACK_COUNT 100
   int countTimeOut = 0;
   int currTecanStatus = 0;
   int lastRecvLen = 0;
   pthread_detach(pthread_self());
   printf("start tecan recv proc thread success\n");
   pCTecan->setExitFlag(0);
   while(!pCTecan->getExitFlag())
   {
      currTecanStatus = pCTecan->getCurrStatus();
      if(  currTecanStatus == tecan_waiting_ack )
      {
         //printf("status:tecan_waiting_ack,lastRecvLen:%d, currLen:%d\n",lastRecvLen,pCTecan->getRecvLen());
         if(pCTecan->getRecvLen() > lastRecvLen)
         {
            countTimeOut = 0;

            lastRecvLen = pCTecan->getRecvLen();
         }
         else
         {
            countTimeOut ++ ;
            if(pCTecan->isRecvComplete() && lastRecvLen==7)
            {
               countTimeOut = 0;
               pCTecan->setCurrStatus(tecan_waiting_res);
               printf("received ack\n");
            }
            else if(pCTecan->isRecvComplete() && lastRecvLen>7)
            {
               countTimeOut = 0;
               //pCTecan->setCurrStatus(tecan_idle);
               //printf("recv ack and response, 111\n");
               pCTecan->setCurrStatus(tecan_waiting_res);
               //lastRecvLen = 0;
            }
            else if(countTimeOut>=MAX_WAIT_ACK_COUNT)
            {
               countTimeOut = 0;
               pCTecan->setCurrStatus(tecan_waiting_res);
               //printf("change from wait ack to wait response\n");
            }
         } 
      }
      else if(currTecanStatus == tecan_waiting_res)
      {
         if(pCTecan->getRecvLen() > lastRecvLen)
         {
            countTimeOut = 0;

            lastRecvLen = pCTecan->getRecvLen();
         }
         else
         {
            countTimeOut ++ ;
            if(pCTecan->isRecvComplete() && lastRecvLen>7)
            {
               countTimeOut = 0;
               tecanRecvMsg * pTecanRecvMsg = NULL;
               unsigned char buffTmp[128];
               int lenTmp;

               //printf("received ack and response222\n");              

               pCTecan->getBuffData(buffTmp,&lenTmp);               

               int retparse = pCTecan->parseFrame(buffTmp,lenTmp,&pTecanRecvMsg);             
               if(retparse==0)
               {
                  tecanRecvMsg * ptrTmp = pTecanRecvMsg;
                  tecanRecvMsg * ptrTmpDel = pTecanRecvMsg;
                  int isComplete = 0;
                  printf("received ack and response,ptrTmp:%p\n",ptrTmp);
                  while(ptrTmp)
                  {
                     //print info here
                     printf("type is %d\n",ptrTmp->resType);
                     if(ptrTmp->resType == CMD_STR_COMP_RES)
                     {                        
                        printf("cmdId:%c,devId:%c,devStatus:%c_%c_%c_%c\n",
                        ptrTmp->un_res.stResCmdStrComp.cmdId,
                        ptrTmp->un_res.stResCmdStrComp.devId,
                        ptrTmp->un_res.stResCmdStrComp.devStatus[0],
                        ptrTmp->un_res.stResCmdStrComp.devStatus[1],
                        ptrTmp->un_res.stResCmdStrComp.devStatus[2],
                        ptrTmp->un_res.stResCmdStrComp.devStatus[3]);

                        if( ('1' == ptrTmp->un_res.stResCmdStrComp.devId
                             && 0x44 == ptrTmp->un_res.stResCmdStrComp.devStatus[1]
                            )
                            ||
                            ('3' == ptrTmp->un_res.stResCmdStrComp.devId
                             && 0x30 == ptrTmp->un_res.stResCmdStrComp.devStatus[1]
                            )
                           )

                        {
                           pCTecan->setLiquidDetect(1);
                        }
                        else
                        {
                           pCTecan->setLiquidDetect(0);                           
                        }
                        

                        isComplete = 1;
                     }
                     ptrTmpDel = ptrTmp;
                     ptrTmp = ptrTmp->next;
                     delete ptrTmpDel;
                  }
                  if(isComplete == 1)
                  {
                       pCTecan->actFinish(0,0);
                  }
               }
               else
               {
                  printf("parse failed\n");
               }
               lastRecvLen = 0;
               pCTecan->setCurrStatus(tecan_idle);
            }
            else if(countTimeOut>=MAX_WAIT_ACK_COUNT)
            {
               countTimeOut = 0;
               pCTecan->setCurrStatus(tecan_idle);
               lastRecvLen = 0;
            }
         }
      }

      usleep(200000);
       //
   }

}

void CTecan::setExitFlag(int flagIn)
{
   m_exitThreadFlag = flagIn;
}

int CTecan::isRecvComplete(void)
{

   if(m_recvLen<7)
   {
      return 0;
   }

   if(m_recvBuff[0] == SOCA_FRAME_HEADER
      &&m_recvBuff[m_recvLen-2] == SOCA_FRAME_TAIL )
   {
      return 1;
   }

}

int CTecan::getRecvLen(void)
{
    return m_recvLen;
}

int CTecan::getBuffData(unsigned char * buffOut,int *lenOut)
{
    pthread_mutex_lock(&m_memMutex);
    memcpy(buffOut,m_recvBuff,m_recvLen);
    *lenOut = m_recvLen;
    m_recvLen = 0;
    memset(m_recvBuff,0,128);
    pthread_mutex_unlock(&m_memMutex);
}

int CTecan::getExitFlag(void)
{
    return m_exitThreadFlag;
}

void CTecan::startRecv(void)
{
   pthread_t thread_hanle;
   pthread_create(&thread_hanle,NULL,funcProcThread,this);
}


int CTecan::cmdAct(void * param,void * canInterface)
{
   ctl_uim * tecanParam = (ctl_uim *)param;
   int opcode = tecanParam->opcode;
   CUim_MainCtl * canCtl = (CUim_MainCtl *)canInterface;

   unsigned char buff[128];
   int len = 0;
   memset(buff,0,128);

   m_msgSrc = tecanParam->msgSrc;

   printf("CTecan::cmdAct,opcode:%d\n",tecanParam->opcode);//CTecan::cmdAct

   switch (tecanParam->opcode)
   {
      case TECAN_Z_MOVE:
      {
         move_param moveParam;
         memset(&moveParam,0,sizeof(move_param));
         
         moveParam.pos = tecanParam->un_cmd.moveCtl.position;
         moveParam.speed = tecanParam->un_cmd.moveCtl.speed;
         moveParam.inc_speed = tecanParam->un_cmd.moveCtl.inc_speed;

         this->setZMoveStr(&moveParam,buff,&len);//&moveParam,
         
      }
      break;
      case TECAN_LOAD_TIP:
      {
         tecanActParam * param = &(tecanParam->un_cmd.tecanCtlData);
         
         this->setLoadTipStr(&(param->tipParam),buff,&len);//&moveParam,
         
         
      }
      break;
      case TECAN_DROP_TIP:
      {
         tecanActParam * param = &(tecanParam->un_cmd.tecanCtlData);

         cmdThreadParam * threadParam = new cmdThreadParam;
         tecan_tip_param * paramTip = new tecan_tip_param;

         memcpy(paramTip,&(tecanParam->un_cmd.tecanCtlData.tipParam),sizeof(tecan_tip_param));
         threadParam->canHanle = canCtl;
         threadParam->ptrTecan = this;
         threadParam->param = paramTip;
         threadParam->opcode = TECAN_DROP_TIP;
         pthread_t handleTmp;
         pthread_create(&handleTmp,NULL,funcCmdThread,threadParam);
      }
      break;
      case TECAN_SUCK:
      {
         //tecanActParam * param = &(tecanParam->un_cmd.tecanCtlData);

         cmdThreadParam * threadParam = new cmdThreadParam;

         //suck_param * paramSuck = new suck_param;

         //memcpy(paramSuck,&(tecanParam->un_cmd.suckParam),sizeof(suck_param));

         tecan_liquid_param * paramLiquid = new tecan_liquid_param;
         memcpy(paramLiquid,&(tecanParam->un_cmd.tecanCtlData.liquidParam),sizeof(tecan_liquid_param));

         threadParam->canHanle = canCtl;
         threadParam->ptrTecan = this;
         threadParam->param = paramLiquid;
         threadParam->opcode = TECAN_SUCK;
         pthread_t handleTmp;
         pthread_create(&handleTmp,NULL,funcCmdThread,threadParam);
      }
      break;
      case TECAN_SPIT:
      {
         // tecan_liquid_param * paramLiquid = &(tecanParam->un_cmd.tecanCtlData.liquidParam);
         
         
         // sprintf((char*)buff,"1/0V40000v4000c4000L35D%f,1R",paramLiquid->quantity);
         // int lenStr = strlen((char*)buff);
         // encap_tecan_frame(buff,lenStr,&len);

         tecanActParam * param = &(tecanParam->un_cmd.tecanCtlData);

         cmdThreadParam * threadParam = new cmdThreadParam;
         tecan_liquid_param * paramLiquid = new tecan_liquid_param;

         memcpy(paramLiquid,&(tecanParam->un_cmd.tecanCtlData.liquidParam),sizeof(tecan_liquid_param));
         threadParam->canHanle = canCtl;
         threadParam->ptrTecan = this;
         threadParam->param = paramLiquid;
         threadParam->opcode = TECAN_SPIT;
         pthread_t handleTmp;
         pthread_create(&handleTmp,NULL,funcCmdThread,threadParam);
      }
      break;

      case TECAN_SUCK_EXT:
      case TECAN_SUCK_P:
      {
         //tecanActParam * param = &(tecanParam->un_cmd.tecanCtlData);

         cmdThreadParam * threadParam = new cmdThreadParam;

         suck_param * paramSuck = new suck_param;

         memcpy(paramSuck,&(tecanParam->un_cmd.suckParam),sizeof(suck_param));

         //tecan_liquid_param * paramLiquid = new tecan_liquid_param;
         //memcpy(paramLiquid,&(tecanParam->un_cmd.tecanCtlData.liquidParam),sizeof(tecan_liquid_param));

         threadParam->canHanle = canCtl;
         threadParam->ptrTecan = this;
         threadParam->param = paramSuck;
         threadParam->opcode = tecanParam->opcode;
         pthread_t handleTmp;
         pthread_create(&handleTmp,NULL,funcCmdThread,threadParam);
      }
      break;
      case TECAN_INIT:
      {
         tecanActParam * param = &(tecanParam->un_cmd.tecanCtlData);
         this->setInitSOCAStr(param->initParam.detectType,buff,&len);//&moveParam,
      }
      break;
      case TECAN_RESET:
      {
         this->setResetAdpStr(buff,&len);
      }
      break;
      case TECAN_SUCK_SIMPLE:
      {
         //suck_param * paramSuck = new suck_param;
         //memcpy(paramSuck,&(tecanParam->un_cmd.suckParam),sizeof(suck_param));
         this->setSuckSimpleStr(&(tecanParam->un_cmd.suckParam),buff,&len);
         
      }
      break;
      case TECAN_SPIT_SIMPLE:
      {
         //suck_param * paramSuck = new suck_param;
         //memcpy(paramSuck,&(tecanParam->un_cmd.suckParam),sizeof(suck_param));
         this->setSpitSimpleStr(&(tecanParam->un_cmd.tecanCtlData.liquidParam),buff,&len);
      }
      break;
      default:
      break;
   }
   // printf("data to tecan, len is %d\n",len);
   // for(int i=0;i<len;i++)
   // {
   //    printf("%02x,",buff[i]);
   //    if(i%8 == 7)
   //    {
   //       printf("\n");
   //    }
   // }

   // if((len-1)%8 != 7)
   // {
   //    printf("\n");
   // }
   if(len>0)
   {
      clearBuff();
      this->setCompleteCB(sendAckToGrpc);
      canCtl->sendString(SID_TECAN,len,buff);
      this->setCurrStatus(tecan_waiting_ack);
   }
   return 0;
}

int CTecan::clearBuff(void)
{
   pthread_mutex_lock(&m_memMutex);
   m_recvLen = 0;
   m_liquid_detected = 0;
   memset(m_recvBuff,0,MAX_TECAN_BUFF_LEN);
   pthread_mutex_unlock(&m_memMutex);
}

void CTecan::setLoadTipStr(void * tipParamIn,unsigned char * buffOut,int *lenOut)
{
    tecan_tip_param * ptrTipParam = (tecan_tip_param *)tipParamIn;
    
    sprintf((char*)buffOut,"%s%dA%dA%dE","1/1a20000s",ptrTipParam->speed,
    ptrTipParam->posTip,ptrTipParam->posRet);

    int tempLen = strlen((char*)buffOut);
    encap_tecan_frame(buffOut,tempLen,lenOut);

}

void CTecan::setDropTipStr(void * tipParamIn,unsigned char * buffOut,int *lenOut)
{
   tecan_tip_param * ptrTipParam = (tecan_tip_param *)tipParamIn;

   sprintf((char*)buffOut,"%s%dE)","1(/0ER/1s4500A",ptrTipParam->posRet);

   int tempLen = strlen((char*)buffOut);
   encap_tecan_frame(buffOut,tempLen,lenOut);
   
}

void CTecan::setSuckSimpleStr(void * suckParamIn,unsigned char * buffOut,int *lenOut)
{
   suck_param * ptrSuckParam = (suck_param *)suckParamIn;
   int tempLen = 0;
   // sprintf((char*)buffOut,"%s%dE)","1(/0ER/1s4500A",ptrTipParam->posRet);

   // int tempLen = strlen((char*)buffOut);
   // encap_tecan_frame(buffOut,tempLen,lenOut);

   if(ptrSuckParam->quantity <= 0 || ptrSuckParam->speedSuck <= 0)
   {
      return;
   }


   float suckSpeed = ptrSuckParam->speedSuck;//ul/s
   int suckSpeed_inc = (int)(suckSpeed*40);//*1000/25; // inc/s

   if(suckSpeed_inc<2000)
   {
      suckSpeed_inc = 2000;
   }
   const float area_cross = 88.24733764; //mm square
   float speedDown_mm = suckSpeed/area_cross;

   const float z_accuracy = 0.09525;//mm/step

   int speedDown = (int)(speedDown_mm/z_accuracy);


   int tempStep = (int)(ptrSuckParam->quantity / area_cross/ z_accuracy);
      
   sprintf((char*)buffOut,"1(/0V%dv2000c2000P%.3f,1R/1s%dR%dE)",
   suckSpeed_inc, ptrSuckParam->quantity,speedDown, tempStep);

   printf("str for suck:%s\n",buffOut);

   tempLen = strlen((char*)buffOut);
   encap_tecan_frame(buffOut,tempLen,lenOut);

}

void CTecan::setSpitSimpleStr(void * spitParamIn,unsigned char * buffOut,int *lenOut)
{
   tecan_liquid_param * ptrLiquidParam = (tecan_liquid_param *)spitParamIn;

   int tempSpeed = ptrLiquidParam->speed;
   //printf("speed recv is %d\n",tempSpeed);
   tempSpeed = tempSpeed>2000?tempSpeed:2000;

   int incSpeed = (tempSpeed>=4000)?4000:tempSpeed;

   if(ptrLiquidParam->quantity <= 0)
   {
      return;
   }

   sprintf((char*)buffOut,"1/0V%dv%dc%dL35D%.3f,1R",      
   tempSpeed,
   incSpeed,
   incSpeed,
   ptrLiquidParam->quantity
   );

   encap_tecan_frame(buffOut,strlen((char *)buffOut),lenOut);
}


void * funcCmdThread(void * paramIn)
{
   cmdThreadParam * ptrThreadParam = (cmdThreadParam *)paramIn;

   int opcode = ptrThreadParam->opcode;

   CUim_MainCtl * canCtl = (CUim_MainCtl *)(ptrThreadParam->canHanle);
   CTecan * tecanHandle = (CTecan *)(ptrThreadParam->ptrTecan);
   pthread_detach(pthread_self());
   if(opcode==TECAN_DROP_TIP)
   {
      tecan_tip_param * ptrTipParam = (tecan_tip_param *)(ptrThreadParam->param);
      
      unsigned char buff[128];
      int len = 0;
      memset(buff,0,128);

      //1, goto position firstly
      sprintf((char*)buff,"1(/0ER/1s%dA%ds%dA%dE)",ptrTipParam->speed, ptrTipParam->posTip,
      ptrTipParam->speed, ptrTipParam->posRet);
      //sprintf((char*)buff,"1/1s%dA%dE",ptrTipParam->speed, ptrTipParam->posTip);
      //sprintf((char*)buff,"1/1s%dA%dE",ptrTipParam->speed, ptrTipParam->posTip);
      int tempLen = strlen((char*)buff);
      tecanHandle->encap_tecan_frame(buff,tempLen,&len);
      // if(len>0)
      // {
      //    tecanHandle->clearBuff();
      //    tecanHandle->setCompleteCB(NULL);
      //    canCtl->sendString(SID_TECAN,len,buff);
      //    tecanHandle->setCurrStatus(tecan_waiting_ack);
      // }

      // int count = 0;
      // while(tecanHandle->getCurrStatus() != tecan_idle)
      // {
      //    count++;
      //    usleep(2000);
      //    if(count>=15000)//500*30
      //    {
      //       break;
      //    }

      //    if(count==5000 || count==10000 || count==13000)
      //    {
      //       printf("count:%d,status is %d\n",count,tecanHandle->getCurrStatus());
      //    }
      // }
      // //2,drop the tip      
      // memset(buff,0,128);
      // len = 0;
      // sprintf((char*)buff,"%s","1/0ER");
      // tempLen = strlen((char*)buff);
      // tecanHandle->encap_tecan_frame(buff,tempLen,&len);
      // if(len>0)
      // {
      //    tecanHandle->clearBuff();
      //    tecanHandle->setCompleteCB(NULL);
      //    canCtl->sendString(SID_TECAN,len,buff);
      //    tecanHandle->setCurrStatus(tecan_waiting_ack);
      // }

      // count = 0;
      // while(tecanHandle->getCurrStatus() != tecan_idle)
      // {
      //    count++;
      //    usleep(2000);
      //    if(count>=15000)//500*30
      //    {
      //       break;
      //    }

      //    if(count==5000 || count==10000 || count==13000)
      //    {
      //       printf("count:%d,status is %d\n",count,tecanHandle->getCurrStatus());
      //    }
      // }
      
       //3, return high
      // memset(buff,0,128);
      // len = 0;
      // sprintf((char*)buff,"1/1s%dA%dE",ptrTipParam->speed,ptrTipParam->posRet);
      // tempLen = strlen((char*)buff);
      // tecanHandle->encap_tecan_frame(buff,tempLen,&len);
      if(len>0)
      {
         tecanHandle->clearBuff();
         tecanHandle->setCompleteCB(sendAckToGrpc);
         canCtl->sendString(SID_TECAN,len,buff);
         tecanHandle->setCurrStatus(tecan_waiting_ack);

      }
      delete ptrTipParam;

   }
   else if(opcode==TECAN_SUCK)
   {
       tecan_liquid_param * ptrLiquidParam = (tecan_liquid_param *)ptrThreadParam->param;
       tecanHandle->actSuckLiquid(ptrLiquidParam,canCtl);
       delete ptrLiquidParam;
   }
   else if(opcode==TECAN_SPIT)
   {
      tecan_liquid_param * ptrLiquidParam = (tecan_liquid_param *)ptrThreadParam->param;
      tecanHandle->actDispenseLiquid(ptrLiquidParam,canCtl);
      delete ptrLiquidParam;
   }
   else if(opcode==TECAN_SUCK_EXT)
   {      
      suck_param *ptrSuckParam = (suck_param *)ptrThreadParam->param;
      tecanHandle->actSuckLiquid_ext(ptrSuckParam,canCtl);
      delete ptrSuckParam;
   }
   else if(opcode==TECAN_SUCK_P)
   {
      suck_param *ptrSuckParam = (suck_param *)ptrThreadParam->param;
      //tecanHandle->actSuckLiquid_p(ptrSuckParam,canCtl);
      tecanHandle->actSuckLiquid_p1(ptrSuckParam,canCtl);
      delete ptrSuckParam;
   }
   else if(opcode==TECAN_SUCK_SIMPLE)
   {
      suck_param *ptrSuckParam = (suck_param *)ptrThreadParam->param;
      //tecanHandle->actSuckLiquid_p(ptrSuckParam,canCtl);
      tecanHandle->actSuckLiquid_p1(ptrSuckParam,canCtl);
      delete ptrSuckParam;
   }

   delete ptrThreadParam;
}

void CTecan::setCompleteCB(competeCallBack ptrFuncIn)
{
    m_funcComplteCB = ptrFuncIn;
}

void CTecan::actFinish(int statusCode,int errorNum)
{
   printf("enter in actFinish, completeCB=%x\n",m_funcComplteCB);
   if(m_funcComplteCB)
   {
      if(m_msgSrc == MSG_FROM_LUA)
      {
         recvDataEleCtl stAckData;
         memset(&stAckData,0,sizeof(recvDataEleCtl));
         stAckData.sid = SID_TECAN;
         if(statusCode==0)
         {
            stAckData.frameType = CHECK_MOVE_END;
         }
         else 
         {
            stAckData.frameType = CHECK_ACT_ERROR;
         }
         
         sendAckToLua(&stAckData);
      }
      else
      {
         m_funcComplteCB(statusCode,errorNum,SID_TECAN,-1);         
      }
      m_funcComplteCB = NULL;

   }
}


void CTecan::actSuckLiquid(void * liquidParamIn, void * canInterFace)
{
      tecan_liquid_param * ptrLiquidParam = (tecan_liquid_param *)liquidParamIn;
      CUim_MainCtl * canCtl = (CUim_MainCtl *)canInterFace;
      unsigned char buff[128];
      int len = 0;
      memset(buff,0,128);

      //1, goto position and detect
      sprintf((char*)buff,"1/1s3000A%ds300G20N2000A%dE",//"1(/0B1R/1F1s2000A%ds300A%dE)",
      ptrLiquidParam->posPre,ptrLiquidParam->posMax);

      int tempLen = strlen((char*)buff);
      encap_tecan_frame(buff,tempLen,&len);
      if(len>0)
      {
         clearBuff();
         canCtl->sendString(SID_TECAN,len,buff);
         setCurrStatus(tecan_waiting_ack);
      }

      int count = 0;
      while(getCurrStatus() != tecan_idle)
      {
         count++;
         usleep(2000);
         if(count>=15000)//500*30
         {
            break;
         }

         if(count==5000 || count==10000 || count==13000)
         {
            printf("count:%d,status is %d\n",count,getCurrStatus());
         }
      }

      if(m_liquid_detected == 0)
      {
         //tecanHandle->setCompleteCB(sendAckToGrpc);
         //detecting liquid failed here
         printf("liquid detect failed\n");
         sendAckToGrpc(4,4,SID_TECAN,-1);
         return;
      }
      //2,suck liquid here
      memset(buff,0,128);
      len = 0;
      //calc the steps to follow up
      int tempStep = int((ptrLiquidParam->quantity * 14)/100); 
      
      int tempSpeed = ptrLiquidParam->speed;
      tempSpeed = (tempSpeed>2000)?tempSpeed:2000;

      

      sprintf((char*)buff,"1(/0V%dv2000c2000P%.3f,1,1R/1s50R%dE)",
      tempSpeed,
      ptrLiquidParam->quantity,tempStep);

      printf("str for suck:%s\n",buff);

      tempLen = strlen((char*)buff);
      encap_tecan_frame(buff,tempLen,&len);

      if(len>0)
      {
         clearBuff();
         canCtl->sendString(SID_TECAN,len,buff);
         setCurrStatus(tecan_waiting_ack);
      }
      count = 0;
      while(getCurrStatus() != tecan_idle)
      {
         count++;
         usleep(2000);
         if(count>=15000)//500*30
         {
            break;
         }

         if(count==5000 || count==10000 || count==13000)
         {
            printf("count:%d,status is %d\n",count,getCurrStatus());
         }
      }

      //goto the ret position

      memset(buff,0,128);
      len = 0; 
      
      sprintf((char*)buff,"1/1s500A%dE",ptrLiquidParam->posPost);

      tempLen = strlen((char*)buff);
      encap_tecan_frame(buff,tempLen,&len);

      if(len>0)
      {
         clearBuff();
         setCompleteCB(sendAckToGrpc);
         canCtl->sendString(SID_TECAN,len,buff);
         setCurrStatus(tecan_waiting_ack);
      }
}


void CTecan::actSuckLiquid_ext(void * suckParamIn, void * canInterFace)
{
      suck_param * ptrSuckParam = (suck_param *)suckParamIn;
      CUim_MainCtl * canCtl = (CUim_MainCtl *)canInterFace;
      unsigned char buff[128];
      int len = 0;
      memset(buff,0,128);

      int count = 0;
      int tempLen = 0;

      //1, goto position and detect liquid
      if(ptrSuckParam->air1Quan > 0.0001)
      {
         //1.1 goto previous position and suck air;
         sprintf((char*)buff,"1(/1s%dA%dE/0P%.3f,1R)",//"1(/0B1R/1F1s2000A%ds300A%dE)",
         ptrSuckParam->speedPre,
         ptrSuckParam->posPre,
         ptrSuckParam->air1Quan);

         printf("str1:%s\n",buff);

         tempLen = strlen((char*)buff);
         encap_tecan_frame(buff,tempLen,&len);
         if(len>0)
         {
            clearBuff();
            canCtl->sendString(SID_TECAN,len,buff);
            setCurrStatus(tecan_waiting_ack);
         }

         
         count = 0;
         while(getCurrStatus() != tecan_idle)
         {
            count++;
            usleep(2000);
            if(count>=15000)//500*30
            {
               break;
            }

            if(count==5000 || count==10000 || count==13000)
            {
               printf("count:%d,status is %d\n",count,getCurrStatus());
            }
         }

         if(count>=15000)
         {
            printf("goto previous position and suck air failed\n");
            sendAckToGrpc(4,10,SID_TECAN,-1);
            return ;
         }

         //1.2 start detect liquid face
         usleep(500);
         memset(buff,0,128);
         len = 0;
         sprintf((char*)buff,"1/1s%dG%dN%dA%dE",//"1(/0B1R/1F1s2000A%ds300A%dE)",
         ptrSuckParam->speedDetect,
         ptrSuckParam->stepBelow,
         ptrSuckParam->threshold,
         ptrSuckParam->posMax);

         printf("str2:%s\n",buff);

         tempLen = strlen((char*)buff);
         encap_tecan_frame(buff,tempLen,&len);
         if(len>0)
         {
            clearBuff();
            canCtl->sendString(SID_TECAN,len,buff);
            setCurrStatus(tecan_waiting_ack);
         }
          
         count = 0;
         while(getCurrStatus() != tecan_idle)
         {
            count++;
            usleep(2000);
            if(count>=15000)//500*30
            {
               break;
            }

            if(count==5000 || count==10000 || count==13000)
            {
               printf("count:%d,status is %d\n",count,getCurrStatus());
            }
         }

         if(m_liquid_detected == 0)
         {
            //tecanHandle->setCompleteCB(sendAckToGrpc);
            //detecting liquid failed here
            printf("liquid detect failed\n");
            sendAckToGrpc(4,11,SID_TECAN,-1);
            return;
         }

      }
      else
      {
         sprintf((char*)buff,"1/1s%dA%ds%dG%dN%dA%dE",//"1(/0B1R/1F1s2000A%ds300A%dE)",
         ptrSuckParam->speedPre,
         ptrSuckParam->posPre,
         ptrSuckParam->speedDetect,
         ptrSuckParam->stepBelow,
         ptrSuckParam->threshold,
         ptrSuckParam->posMax);

         tempLen = strlen((char*)buff);
         encap_tecan_frame(buff,tempLen,&len);
         if(len>0)
         {
            clearBuff();
            canCtl->sendString(SID_TECAN,len,buff);
            setCurrStatus(tecan_waiting_ack);
         }

         count = 0;
         while(getCurrStatus() != tecan_idle)
         {
            count++;
            usleep(2000);
            if(count>=15000)//500*30
            {
               break;
            }

            if(count==5000 || count==10000 || count==13000)
            {
               printf("count:%d,status is %d\n",count,getCurrStatus());
            }
         }

         if(m_liquid_detected == 0)
         {
            //tecanHandle->setCompleteCB(sendAckToGrpc);
            //detecting liquid failed here
            printf("liquid detect failed\n");
            sendAckToGrpc(4,11,SID_TECAN,-1);
            return;
         }
      }     

      
      //2,suck liquid here
      memset(buff,0,128);
      len = 0;
      //calc the steps to follow up      

      float suckSpeed = ptrSuckParam->speedSuck;//ul/s
      int suckSpeed_inc = (int)(suckSpeed*40);//*1000/25; // inc/s

      if(suckSpeed_inc<2000)
      {
         suckSpeed_inc = 2000;
      }
      const float area_cross = 88.24733764; //mm square
      float speedDown_mm = suckSpeed/area_cross;

      const float z_accuracy = 0.09525;//mm/step

      int speedDown = (int)(speedDown_mm/z_accuracy);


      int tempStep = (int)(ptrSuckParam->quantity / area_cross/ z_accuracy);
       
      sprintf((char*)buff,"1(/0V%dv2000c2000P%.3f,1R/1s%dR%dE)",
      suckSpeed_inc, ptrSuckParam->quantity,speedDown, tempStep);

      printf("str for suck:%s\n",buff);

      tempLen = strlen((char*)buff);
      encap_tecan_frame(buff,tempLen,&len);

      if(len>0)
      {
         clearBuff();
         canCtl->sendString(SID_TECAN,len,buff);
         setCurrStatus(tecan_waiting_ack);
      }
      count = 0;
      while(getCurrStatus() != tecan_idle)
      {
         count++;
         usleep(2000);
         if(count>=15000)//500*30
         {
            break;
         }

         if(count==5000 || count==10000 || count==13000)
         {
            printf("count:%d,status is %d\n",count,getCurrStatus());
         }
      }
      if(count>=15000)
      {
         printf("suck liquid failed\n");
         sendAckToGrpc(4,12,SID_TECAN,-1);
         return ;
      }

      usleep(500000);
      //goto the ret position

      memset(buff,0,128);
      len = 0; 
      
      sprintf((char*)buff,"1/1s%dA%dE",ptrSuckParam->speedPost, ptrSuckParam->posPost);

      tempLen = strlen((char*)buff);
      encap_tecan_frame(buff,tempLen,&len);

      if(len>0)
      {
         clearBuff();
         //setCompleteCB(sendAckToGrpc);
         canCtl->sendString(SID_TECAN,len,buff);
         setCurrStatus(tecan_waiting_ack);
      }
      count = 0;
      while(getCurrStatus() != tecan_idle)
      {
         count++;
         usleep(2000);
         if(count>=15000)//500*30
         {
            break;
         }

         if(count==5000 || count==10000 || count==13000)
         {
            printf("count:%d,status is %d\n",count,getCurrStatus());
         }
      }
      if(count>=15000)
      {
         printf("go back failed\n");
         sendAckToGrpc(4,13,SID_TECAN,-1);
         return ;
      }

      //suck air
      if(ptrSuckParam->air2Quan<0.0001)
      {
         sendAckToGrpc(0,0,SID_TECAN,-1);
      }
      else
      {
         memset(buff,0,128);
         len = 0; 
         
         sprintf((char*)buff,"1/0P%.3f,1R",ptrSuckParam->air2Quan);

         tempLen = strlen((char*)buff);
         encap_tecan_frame(buff,tempLen,&len);

         if(len>0)
         {
            clearBuff();
            setCompleteCB(sendAckToGrpc);
            canCtl->sendString(SID_TECAN,len,buff);
            setCurrStatus(tecan_waiting_ack);
         }
      }
      
      
}
int CTecan::sendAndWaitAck(unsigned char * strIn, void * canInterface)
{
   int ret = 0;

   if(strIn==NULL || canInterface==NULL)
   {
       ret = 1;
       return ret;
   }
   CUim_MainCtl * canCtl = (CUim_MainCtl *)canInterface;
   int tempLen = strlen((char*)strIn);
   int len = 0;
   encap_tecan_frame(strIn,tempLen,&len);
   if(len>0)
   {
      clearBuff();
      canCtl->sendString(SID_TECAN,len,strIn);
      setCurrStatus(tecan_waiting_ack);
   }
      
   int count = 0;
   while(getCurrStatus() != tecan_idle)
   {
      count++;
      usleep(2000);
      if(count>=15000)//500*30
      {
         break;
      }

      if(count==5000 || count==10000 || count==13000)
      {
         printf("count:%d,status is %d\n",count,getCurrStatus());
      }
   }

   if(count>=15000)
   {
      ret = 2;
   }

   return ret;

}

void CTecan::actSuckLiquid_p(void * suckParamIn, void * canInterFace)
{
   suck_param * ptrSuckParam = (suck_param *)suckParamIn;
      CUim_MainCtl * canCtl = (CUim_MainCtl *)canInterFace;
      unsigned char buff[128];
      int len = 0;
      memset(buff,0,128);

      int count = 0;
      int tempLen = 0;
      int sendRet = 0;

      //1, goto position and detect liquid
      if(ptrSuckParam->air1Quan > 0.0001)
      {
         //1.1 goto previous position and suck air;
         if(ptrSuckParam->air1Quan > 10)
         {
            sprintf((char*)buff,"1(/1s%dA%dE/0P%.3f,1R)",//"1(/0B1R/1F1s2000A%ds300A%dE)",
         ptrSuckParam->speedPre,
         ptrSuckParam->posPre,
         ptrSuckParam->air1Quan);

         }
         else
         {
            int airQuanInt = (int)(ptrSuckParam->air1Quan + 0.5);
            sprintf((char*)buff,"1(/1s%dA%dE/0P%d,1R)",//"1(/0B1R/1F1s2000A%ds300A%dE)",
         ptrSuckParam->speedPre,
         ptrSuckParam->posPre,
         airQuanInt);
         }
         

         printf("str1:%s\n",buff);

         sendRet = sendAndWaitAck(buff,canCtl);

         if(sendRet)
         {
            printf("goto previous position and suck air failed, retcode:%d\n",sendRet);
            sendAckToGrpc(4,10,SID_TECAN,-1);
            return ;
         }

         //1.2 start detect liquid face
         //usleep(500);
         memset(buff,0,128);
         len = 0;
         sprintf((char*)buff,"1(/0B1p0,%dR/1F1s%dA%dE)",//"1(/0B1R/1F1s2000A%ds300A%dE)",
         ptrSuckParam->threshold,
         ptrSuckParam->speedDetect,         
         ptrSuckParam->posMax);

         printf("str2:%s\n",buff);
         
         sendRet = sendAndWaitAck(buff,canCtl);

         if(sendRet)
         {
            printf("liquid detect act failed,retcode:%d\n",sendRet);
            sendAckToGrpc(4,14,SID_TECAN,-1);
            return;
         }

         if(m_liquid_detected == 0)
         {
            //tecanHandle->setCompleteCB(sendAckToGrpc);
            //detecting liquid failed here
            printf("liquid detect failed\n");
            sendAckToGrpc(4,11,SID_TECAN,-1);
            return;
         }

      }
      else
      {
         sprintf((char*)buff,"1(/0B1p0,%dR/1F1s%dA%ds%dA%dE)",//"1(/0B1R/1F1s2000A%ds300A%dE)",
         ptrSuckParam->threshold,
         ptrSuckParam->speedPre,
         ptrSuckParam->posPre,
         ptrSuckParam->speedDetect,         
         ptrSuckParam->posMax);

         printf("str3:%s\n",buff);

         sendRet = sendAndWaitAck(buff,canCtl);

         if(sendRet)
         {
            printf("liquid detect act failed,retcode:%d\n",sendRet);
            sendAckToGrpc(4,12,SID_TECAN,-1);
            return;
         }

         if(m_liquid_detected == 0)
         {
            //tecanHandle->setCompleteCB(sendAckToGrpc);
            //detecting liquid failed here
            printf("liquid detect failed\n");
            sendAckToGrpc(4,13,SID_TECAN,-1);
            return;
         }
      }

      //2, go on down
      memset(buff,0,128);
      len = 0;
      sprintf((char*)buff,"1/1s50R%dE",//"1(/0B1R/1F1s2000A%ds300A%dE)",
         ptrSuckParam->stepBelow);    
      
      printf("str4:%s\n",buff);

      sendRet = sendAndWaitAck(buff,canCtl);

      if(sendRet)
      {
         printf("go on down failed,retcode:%d\n",sendRet);
         sendAckToGrpc(4,15,SID_TECAN,-1);
         return;
      }
      
      //2.1,suck liquid here
      memset(buff,0,128);
      len = 0;
      //calc the steps to follow up      

      float suckSpeed = ptrSuckParam->speedSuck;//ul/s
      int suckSpeed_inc = (int)(suckSpeed*40);//*1000/25; // inc/s

      if(suckSpeed_inc<2000)
      {
         suckSpeed_inc = 2000;
      }
      const float area_cross = 88.24733764; //mm square
      float speedDown_mm = suckSpeed/area_cross;

      const float z_accuracy = 0.09525;//mm/step

      int speedDown = (int)(speedDown_mm/z_accuracy);
      
      //compensate the error
      //float quantityTmp = (ptrSuckParam->quantity + 1.7654)/0.9518;

      //no compensate
      float quantityTmp = ptrSuckParam->quantity;

      quantityTmp += ptrSuckParam->more;

      int tempStep = (int)(quantityTmp / area_cross/ z_accuracy);
      
      if(speedDown==0)
      {
         speedDown = 1;
      }

      if(tempStep==0)
      {
         tempStep = 1;
      }

      if(quantityTmp>10)
      {
         sprintf((char*)buff,"1(/0V%dv2000c2000P%.3f,1R/1s%dR%dE)",
           suckSpeed_inc, quantityTmp,speedDown, tempStep);
      }
      else
      {
         int quantityInt = (int)(quantityTmp+0.5);
         sprintf((char*)buff,"1(/0V%dv2000c2000P%d,1R/1s%dR%dE)",
           suckSpeed_inc, quantityInt,speedDown, tempStep);
      }      

      printf("str for suck:%s\n",buff);

      sendRet = sendAndWaitAck(buff,canCtl);

      if(sendRet)
      {
         printf("suck failed,retcode:%d\n",sendRet);
         sendAckToGrpc(4,16,SID_TECAN,-1);
         return;
      }

      usleep(200000);
      

      //2.1,spit the redundant liquid here
      memset(buff,0,128);
      len = 0;
      int tempSpitQuan = (int)(ptrSuckParam->spitQuan+0.5);
      
      sprintf((char*)buff,"1/0V%dv%dc%dL35D%d,1R",      
      2000,
      1000,
      2000,
      tempSpitQuan
      );

      printf("str for spit reduntant:%s\n",buff);
      sendRet = sendAndWaitAck(buff,canCtl);

      if(sendRet)
      {
         printf("spit failed,retcode:%d\n",sendRet);
         sendAckToGrpc(4,17,SID_TECAN,-1);
         return;
      }


      //3,goto the ret position

      memset(buff,0,128);
      len = 0; 
      
      sprintf((char*)buff,"1/1s%dA%dE",ptrSuckParam->speedPost, ptrSuckParam->posPost);
      
      printf("str5 suck:%s\n",buff);

      if(ptrSuckParam->air2Quan<0.0001)
      {
         tempLen = strlen((char*)buff);
         encap_tecan_frame(buff,tempLen,&len);

         if(len>0)
         {
            clearBuff();
            setCompleteCB(sendAckToGrpc);
            canCtl->sendString(SID_TECAN,len,buff);
            setCurrStatus(tecan_waiting_ack);
         }
      }
      else
      {
         sendRet = sendAndWaitAck(buff,canCtl);

         if(sendRet)
         {
            printf("goto post position failed,retcode:%d\n",sendRet);
            sendAckToGrpc(4,18,SID_TECAN,-1);
            return;
         }         

         //4,suck air
         memset(buff,0,128);
         len = 0; 
         if(ptrSuckParam->air2Quan>10)
         {
            sprintf((char*)buff,"1/0P%.3f,1R",ptrSuckParam->air2Quan);
         }
         else
         {
            int quantityInt = (int)(ptrSuckParam->air2Quan+0.5);
            sprintf((char*)buff,"1/0P%d,1R",quantityInt);
         }
         
         

         tempLen = strlen((char*)buff);
         encap_tecan_frame(buff,tempLen,&len);

         if(len>0)
         {
            clearBuff();
            setCompleteCB(sendAckToGrpc);
            canCtl->sendString(SID_TECAN,len,buff);
            setCurrStatus(tecan_waiting_ack);
         }

      }      
}

void CTecan::actSuckLiquid_p1(void * suckParamIn, void * canInterFace)
{
   suck_param * ptrSuckParam = (suck_param *)suckParamIn;
      CUim_MainCtl * canCtl = (CUim_MainCtl *)canInterFace;
      unsigned char buff[128];
      int len = 0;
      memset(buff,0,128);

      int count = 0;
      int tempLen = 0;
      int sendRet = 0;

      //setCompleteCB(NULL);

      //1, goto position and detect liquid
      if(ptrSuckParam->air1Quan > 0.0001)
      {
         //1.1 goto previous position and suck air;
         // if(ptrSuckParam->air1Quan > 10)
         // {
         //    sprintf((char*)buff,"1(/1s%dA%dE/0P%.3f,1R)",//"1(/0B1R/1F1s2000A%ds300A%dE)",
         // ptrSuckParam->speedPre,
         // ptrSuckParam->posPre,
         // ptrSuckParam->air1Quan);

         // }
         // else
         // {
         //    int airQuanInt = (int)(ptrSuckParam->air1Quan + 0.5);
         //    sprintf((char*)buff,"1(/1s%dA%dE/0P%d,1R)",//"1(/0B1R/1F1s2000A%ds300A%dE)",
         // ptrSuckParam->speedPre,
         // ptrSuckParam->posPre,
         // airQuanInt);
         // }
         //1.1.1 goto precious position
         sprintf((char*)buff,"1/1s%dA%dE",//"1(/0B1R/1F1s2000A%ds300A%dE)",
         ptrSuckParam->speedPre,
         ptrSuckParam->posPre);
         

         printf("str1:%s\n",buff);

         sendRet = sendAndWaitAck(buff,canCtl);

         if(sendRet)
         {
            printf("goto previous position failed, retcode:%d\n",sendRet);
            sendAckToGrpc(4,10,SID_TECAN,-1);
            return ;
         }
         //1.1.2 suck air
         memset(buff,0,128);
         float suckSpeed = ptrSuckParam->speedSuckAir1;//ul/s
         int suckSpeed_inc = (int)(suckSpeed*40);//*1000/25; // inc/s

         if(suckSpeed_inc<2000)
         {
            suckSpeed_inc = 2000;
         }

         int airQuanInt = (int)(ptrSuckParam->air1Quan + 0.5);
         sprintf((char*)buff,"1/0V%dv2000c2000P%d,1R",//"1(/0B1R/1F1s2000A%ds300A%dE)",
         suckSpeed_inc,airQuanInt);
         

         printf("str suck air1:%s\n",buff);

         sendRet = sendAndWaitAck(buff,canCtl);

         if(sendRet)
         {
            printf("goto previous position and suck air failed, retcode:%d\n",sendRet);
            sendAckToGrpc(4,10,SID_TECAN,-1);
            return ;
         }


         //1.2 start detect liquid face
         //usleep(500);
         memset(buff,0,128);
         len = 0;
         sprintf((char*)buff,"1(/0B1p0,%dR/1F1s%dA%dE)",//"1(/0B1R/1F1s2000A%ds300A%dE)",
         ptrSuckParam->threshold,
         ptrSuckParam->speedDetect,         
         ptrSuckParam->posMax);

         printf("str2:%s\n",buff);
         
         sendRet = sendAndWaitAck(buff,canCtl);

         if(sendRet)
         {
            printf("liquid detect act failed,retcode:%d\n",sendRet);
            sendAckToGrpc(4,14,SID_TECAN,-1);
            return;
         }

         if(m_liquid_detected == 0)
         {
            //tecanHandle->setCompleteCB(sendAckToGrpc);
            //detecting liquid failed here
            printf("liquid detect failed\n");
            sendAckToGrpc(4,11,SID_TECAN,-1);
            return;
         }

      }
      else
      //1, 
      {
         sprintf((char*)buff,"1(/0B1p0,%dR/1F1s%dA%ds%dA%dE)",//"1(/0B1R/1F1s2000A%ds300A%dE)",
         ptrSuckParam->threshold,
         ptrSuckParam->speedPre,
         ptrSuckParam->posPre,
         ptrSuckParam->speedDetect,         
         ptrSuckParam->posMax);

         printf("str3:%s\n",buff);

         sendRet = sendAndWaitAck(buff,canCtl);

         if(sendRet)
         {
            printf("liquid detect act failed,retcode:%d\n",sendRet);
            sendAckToGrpc(4,12,SID_TECAN,-1);
            return;
         }

         if(m_liquid_detected == 0)
         {
            //tecanHandle->setCompleteCB(sendAckToGrpc);
            //detecting liquid failed here
            printf("liquid detect failed\n");
            sendAckToGrpc(4,13,SID_TECAN,-1);
            return;
         }
      }

      //2, go on down
      memset(buff,0,128);
      len = 0;
      sprintf((char*)buff,"1/1s50R%dE",//"1(/0B1R/1F1s2000A%ds300A%dE)",
         ptrSuckParam->stepBelow);    
      
      printf("str4:%s\n",buff);

      sendRet = sendAndWaitAck(buff,canCtl);

      if(sendRet)
      {
         printf("go on down failed,retcode:%d\n",sendRet);
         sendAckToGrpc(4,15,SID_TECAN,-1);
         return;
      }
      
      //2.1,suck liquid here
      memset(buff,0,128);
      len = 0;
      //calc the steps to follow up      

      float suckSpeed = ptrSuckParam->speedSuck;//ul/s
      int suckSpeed_inc = (int)(suckSpeed*40);//*1000/25; // inc/s

      if(suckSpeed_inc<2000)
      {
         suckSpeed_inc = 2000;
      }
      const float area_cross = 88.24733764; //mm square
      float speedDown_mm = suckSpeed/area_cross;

      const float z_accuracy = 0.09525;//mm/step

      int speedDown = (int)(speedDown_mm/z_accuracy);
      
      //compensate the error
      //float quantityTmp = (ptrSuckParam->quantity + 1.7654)/0.9518;

      //no compensate
      float quantityTmp = ptrSuckParam->quantity;

      quantityTmp += ptrSuckParam->more;

      int tempStep = (int)(quantityTmp / area_cross/ z_accuracy);
      
      if(speedDown==0)
      {
         speedDown = 1;
      }

      if(tempStep==0)
      {
         tempStep = 1;
      }

      if(quantityTmp>10)
      {
         sprintf((char*)buff,"1(/0V%dv2000c2000P%.3f,1R/1s%dR%dE)",
           suckSpeed_inc, quantityTmp,speedDown, tempStep);
      }
      else
      {
         int quantityInt = (int)(quantityTmp+0.5);
         sprintf((char*)buff,"1(/0V%dv2000c2000P%d,1R/1s%dR%dE)",
           suckSpeed_inc, quantityInt,speedDown, tempStep);
      }      

      printf("str for suck:%s\n",buff);

      sendRet = sendAndWaitAck(buff,canCtl);

      if(sendRet)
      {
         printf("suck failed,retcode:%d\n",sendRet);
         sendAckToGrpc(4,16,SID_TECAN,-1);
         return;
      }
      //delay after suck
      usleep(ptrSuckParam->delayAfterSuck*1000);
      

      //2.1,spit the redundant liquid here
      memset(buff,0,128);
      len = 0;
      int tempSpitQuan = (int)(ptrSuckParam->spitQuan+0.5);

      int spitSpeed = ptrSuckParam->spitSpeed;//ul/s
      
      if(spitSpeed<2000)
      {
         spitSpeed = 2000;
      }
      sprintf((char*)buff,"1/0V%dv%dc%dL35D%.3f,1R",      
      spitSpeed,
      2000,
      2000,
      ptrSuckParam->spitQuan
      );

      printf("str for spit reduntant:%s\n",buff);
      sendRet = sendAndWaitAck(buff,canCtl);

      if(sendRet)
      {
         printf("spit failed,retcode:%d\n",sendRet);
         sendAckToGrpc(4,17,SID_TECAN,-1);
         return;
      }
      //delay after spit
      usleep(ptrSuckParam->delayAfterSpit*1000);


      //3,goto the ret position

      memset(buff,0,128);
      len = 0; 
      
      sprintf((char*)buff,"1/1s%dA%dE",ptrSuckParam->speedPost, ptrSuckParam->posPost);
      
      printf("str5 goto post position:%s\n",buff);

      if(ptrSuckParam->air2Quan<0.0001)
      {
         tempLen = strlen((char*)buff);
         encap_tecan_frame(buff,tempLen,&len);

         if(len>0)
         {
            clearBuff();
            setCompleteCB(sendAckToGrpc);
            canCtl->sendString(SID_TECAN,len,buff);
            setCurrStatus(tecan_waiting_ack);
         }
      }
      else
      {
         sendRet = sendAndWaitAck(buff,canCtl);

         if(sendRet)
         {
            printf("goto post position failed,retcode:%d\n",sendRet);
            sendAckToGrpc(4,18,SID_TECAN,-1);
            return;
         }         

         //4,suck air 
         memset(buff,0,128);

         suckSpeed = ptrSuckParam->speedSuckAir2;//ul/s
         suckSpeed_inc = (int)(suckSpeed*40);//*1000/25; // inc/s

         if(suckSpeed_inc<2000)
         {
            suckSpeed_inc = 2000;
         }

         len = 0; 
         if(ptrSuckParam->air2Quan>10)
         {
            sprintf((char*)buff,"1/0V%dP%.3f,1R",suckSpeed_inc,ptrSuckParam->air2Quan);
         }
         else
         {
            int quantityInt = (int)(ptrSuckParam->air2Quan+0.5);
            sprintf((char*)buff,"1/0V%dP%d,1R",suckSpeed_inc,quantityInt);
         }
         
         
         printf("str for suck air2:%s\n",buff);
         tempLen = strlen((char*)buff);
         encap_tecan_frame(buff,tempLen,&len);

         if(len>0)
         {
            clearBuff();
            setCompleteCB(sendAckToGrpc);
            canCtl->sendString(SID_TECAN,len,buff);
            setCurrStatus(tecan_waiting_ack);
         }

      }      
}

void CTecan::actDispenseLiquid(void * liquidParamIn, void * canInterFace)
{
      tecan_liquid_param * ptrLiquidParam = (tecan_liquid_param *)liquidParamIn;
      CUim_MainCtl * canCtl = (CUim_MainCtl *)canInterFace;
      unsigned char buff[128];
      int len = 0;
      memset(buff,0,128);

      //1, goto previous position
      sprintf((char*)buff,"1/1s4500A%dE",ptrLiquidParam->posPre);

      printf("str go to spit pos:%s\n",buff);

      int tempLen = strlen((char*)buff);
      encap_tecan_frame(buff,tempLen,&len);
      if(len>0)
      {
         clearBuff();
         canCtl->sendString(SID_TECAN,len,buff);
         setCurrStatus(tecan_waiting_ack);
      }

      int count = 0;
      while(getCurrStatus() != tecan_idle)
      {
         count++;
         usleep(2000);
         if(count>=15000)//500*30
         {
            break;
         }

         if(count==5000 || count==10000 || count==13000)
         {
            printf("count:%d,status is %d\n",count,getCurrStatus());
         }
      }
      //2.1,dispense air here
      if(ptrLiquidParam->quanSpitAir1 > 0.001)
      {
         memset(buff,0,128);
         len = 0;
         
         int tempSpeed = ptrLiquidParam->speed;
         printf("speed recv is %d\n",tempSpeed);
         tempSpeed = tempSpeed>2000?tempSpeed:2000;

         int incSpeed = (tempSpeed>=4000)?4000:tempSpeed;

         int tempSpitQuan = (int)(ptrLiquidParam->quanSpitAir1 + 0.5);

         sprintf((char*)buff,"1/0V%dv%dc%dL35D%.3f,1R",      
         tempSpeed,
         incSpeed,
         incSpeed,
         ptrLiquidParam->quanSpitAir1
         );

         printf("str for spit air:%s\n",buff);

         //printf("str for spit:%s\n",buff);
         tempLen = strlen((char*)buff);
         encap_tecan_frame(buff,tempLen,&len);

         if(len>0)
         {
            clearBuff();
            canCtl->sendString(SID_TECAN,len,buff);
            setCurrStatus(tecan_waiting_ack);
         }
         count = 0;
         while(getCurrStatus() != tecan_idle)
         {
            count++;
            usleep(2000);
            if(count>=15000)//500*30
            {
               break;
            }

            if(count==5000 || count==10000 || count==13000)
            {
               printf("count:%d,status is %d\n",count,getCurrStatus());
            }
         }

      }
      
      //2.2,dispense liquid here
      memset(buff,0,128);
      len = 0;
      
      int tempSpeed = ptrLiquidParam->speed;
      printf("speed recv is %d\n",tempSpeed);
      tempSpeed = tempSpeed>2000?tempSpeed:2000;

      int incSpeed = (tempSpeed>=4000)?4000:tempSpeed;


      sprintf((char*)buff,"1/0V%dv%dc%dL35D%.3f,1R",      
      tempSpeed,
      incSpeed,
      incSpeed,
      ptrLiquidParam->quantity
      );

      printf("str for spit:%s\n",buff);

      //printf("str for spit:%s\n",buff);
      tempLen = strlen((char*)buff);
      encap_tecan_frame(buff,tempLen,&len);

      if(len>0)
      {
         clearBuff();
         canCtl->sendString(SID_TECAN,len,buff);
         setCurrStatus(tecan_waiting_ack);
      }
      count = 0;
      while(getCurrStatus() != tecan_idle)
      {
         count++;
         usleep(2000);
         if(count>=15000)//500*30
         {
            break;
         }

         if(count==5000 || count==10000 || count==13000)
         {
            printf("count:%d,status is %d\n",count,getCurrStatus());
         }
      }

      //delay after spit
      usleep(ptrLiquidParam->delayAfterSpit*1000);

      //3,goto the ret position

      memset(buff,0,128);
      len = 0; 
      
      sprintf((char*)buff,"1/1s%dA%dE",ptrLiquidParam->speedGoUp,ptrLiquidParam->posPost);
      
      printf("str go to posPost:%s\n",buff);
      tempLen = strlen((char*)buff);
      encap_tecan_frame(buff,tempLen,&len);

      if(len>0)
      {
         clearBuff();
         if(ptrLiquidParam->quanSuckBack < 0.005)
         {
            setCompleteCB(sendAckToGrpc);
         }
         
         canCtl->sendString(SID_TECAN,len,buff);
         setCurrStatus(tecan_waiting_ack);
      }
      //4,after dispense, suck air to avoid liquid remain leak

      if(ptrLiquidParam->quanSuckBack >= 0.005)
      {
         count = 0;
         while(getCurrStatus() != tecan_idle)
         {
            count++;
            usleep(2000);
            if(count>=15000)//500*30
            {
               break;
            }

            if(count==5000 || count==10000 || count==13000)
            {
               printf("count:%d,status is %d\n",count,getCurrStatus());
            }
         }

         memset(buff,0,128);
         len = 0; 
         int airQuantity = (int)(ptrLiquidParam->quanSuckBack + 0.5);


         float speed_suck = ptrLiquidParam->speedSuckBack;
         int speed_inc = (int)(speed_suck*40);

         if(speed_inc<2000)
         {
            speed_inc = 2000;
         }
         
         sprintf((char*)buff,"1/0V%dv2000c2000P%d,1R",speed_inc,airQuantity);    
         
         printf("str for suckback:%s\n",buff);
         tempLen = strlen((char*)buff);
         encap_tecan_frame(buff,tempLen,&len);

         if(len>0)
         {
            clearBuff();
            setCompleteCB(sendAckToGrpc);
            canCtl->sendString(SID_TECAN,len,buff);
            setCurrStatus(tecan_waiting_ack);
         }
      }


}